﻿using System.Globalization;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
using System.Net;
using System.Reflection;
using Devonline.Utils;
using Mapster;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.DynamicLinq;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;

namespace Devonline.AspNetCore;

/// <summary>
/// 公共数据处理服务
/// </summary>
/// <typeparam name="TDbContext">数据库上下文</typeparam>
/// <typeparam name="TKey">键类型</typeparam>
public class ServiceBase<TDbContext, TKey> : IServiceBase<TDbContext, TKey> where TDbContext : DbContext where TKey : IConvertible
{
    protected readonly TDbContext _context;
    protected readonly ILogger<ServiceBase<TDbContext, TKey>> _logger;
    protected readonly IDistributedCache _cache;
    protected readonly AppSetting _appSetting;
    protected readonly HttpContext _httpContext;
    protected readonly HttpRequest _request;
    protected bool _enableCache;

    /// <summary>
    /// 构造方法
    /// </summary>
    /// <param name="logger">日志</param>
    /// <param name="context">数据库上下文</param>
    /// <param name="httpContextAccessor">Http上下文</param>
    /// <param name="cache">缓存</param>
    /// <param name="appSetting">配置</param>
    public ServiceBase(
        TDbContext context,
        ILogger<ServiceBase<TDbContext, TKey>> logger,
        IDistributedCache cache,
        IHttpContextAccessor httpContextAccessor,
        AppSetting appSetting
        )
    {
        if (httpContextAccessor.HttpContext == null)
        {
            throw new ArgumentNullException(nameof(httpContextAccessor.HttpContext), "HttpContext can't be null!");
        }

        _context = context;
        _logger = logger;
        _cache = cache;
        _appSetting = appSetting;
        _httpContext = httpContextAccessor.HttpContext;
        _request = _httpContext.Request;
        _enableCache = _appSetting.EnableCache;
        UserName = _httpContext.User.Identity?.Name ?? AppSettings.USER_ANONYMOUS;
        AdapterConfig = new();
    }

    #region 基础方法
    /// <summary>
    /// TypeAdapterConfig 类型隐射实例
    /// </summary>
    public virtual TypeAdapterConfig AdapterConfig { get; init; }
    /// <summary>
    /// Gets or sets a flag indicating if changes should be persisted after CreateAsync, 
    /// UpdateAsync and DeleteAsync are called.
    /// True if changes should be automatically persisted, otherwise false.
    /// </summary>
    public virtual bool AutoSaveChanges { get; set; } = true;
    /// <summary>
    /// 当前登录的用户
    /// </summary>
    public virtual string UserName { get; private set; }
    /// <summary>
    /// 从 httpContext 获取用户标识, 用户标识在 User.Claims 中的 type 为 sub; 在 request 中为 userId
    /// </summary>
    /// <returns></returns>
    public virtual string? GetUserId() => _httpContext.GetUserId();
    /// <summary>
    /// 从 httpContext 获取用户标识, 用户标识在 User.Claims 中的 type 为 userName
    /// </summary>
    /// <returns></returns>
    public virtual string? GetUserName() => _httpContext.GetUserName();
    /// <summary>
    /// 获取查询选项列表
    /// </summary>
    /// <returns>返回合并后的查询选项</returns>
    public virtual IEnumerable<KeyValuePair<string, StringValues>> GetRequestOptions()
    {
        var queryString = _request.Query.ToList();
        if (_request.HasFormContentType && (_request.Form.Keys?.Count ?? 0) > 0)
        {
            queryString.AddRange(_request.Form);
        }

        if (_request.Headers.IsNotNullOrEmpty())
        {
            queryString.AddRange(_request.Headers);
        }

        if (_request.Cookies.IsNotNullOrEmpty())
        {
            queryString.AddRange(_request.Cookies.ToDictionary(x => x.Key, x => new StringValues(x.Value)));
        }

        return queryString;
    }
    /// <summary>
    /// 从当前 request 中获取参数的值, 取值顺序 Query -> Form -> Header -> Cookie
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public virtual T? GetRequestOption<T>(string key) where T : IConvertible
    {
        var value = string.Empty;
        if (_request.Query.Keys.Contains(key))
        {
            value = _request.Query[key];
        }
        else if (_request.HasFormContentType && _request.Form.Keys.IsNotNullOrEmpty() && _request.Form.Keys.Contains(key))
        {
            value = _request.Form[key];
        }
        else if (_request.Headers.IsNotNullOrEmpty() && _request.Headers.ContainsKey(key))
        {
            value = _request.Headers[key];
        }
        else if (_request.Cookies.IsNotNullOrEmpty() && _request.Cookies.Keys.Contains(key))
        {
            value = _request.Cookies[key];
        }

        return string.IsNullOrWhiteSpace(value) ? default : value.To<T>();
    }
    /// <summary>
    /// 获取上下文对象中参数/变量的值, 取值顺序 HttpContext -> Query -> Form -> Header -> Cookie
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public virtual T? GetContextOption<T>(string key) where T : IConvertible
    {
        var value = _httpContext.User.Claims.FirstOrDefault(c => c.Type == key)?.Value;
        return string.IsNullOrWhiteSpace(value) ? _httpContext.Request.GetRequestOption<T>(key) : value.To<T>();
    }
    /// <summary>
    /// 获取附件绝对地址
    /// </summary>
    /// <param name="fileName">附件文件名</param>
    /// <returns></returns>
    public virtual string GetAttachmentPath(string fileName) => Path.Combine((_appSetting.Attachment?.RootPath ?? AppSettings.DEFAULT_ATTACHMENT_PATH).GetAbsolutePath(), fileName);
    /// <summary>
    /// 获取随机的临时文件名
    /// </summary>
    /// <param name="extension">文件扩展名</param>
    /// <returns></returns>
    public virtual string GetTempFileName(string? extension = default)
    {
        var filePath = DateTime.UtcNow.ToString(AppSettings.DEFAULT_DATE_FORMAT, CultureInfo.InvariantCulture);
        var attachmentPath = GetAttachmentPath(filePath);
        if (!Directory.Exists(attachmentPath))
        {
            Directory.CreateDirectory(attachmentPath);
        }

        extension ??= AppSettings.DEFAULT_TEMPORARY_FILE_EXTENSION;
        return Path.Combine(filePath, KeyGenerator.GetKey() + extension);
    }
    /// <summary>
    /// 获取 TypeAdapterConfig 默认实例
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <returns></returns>
    public virtual TypeAdapterConfig GetAdapterConfig<TEntitySet>()
    {
        var config = new TypeAdapterConfig();
        var typeConfig = config.ForDestinationType<TEntitySet>()
            //.PreserveReference(true)
            //.ShallowCopyForSameType(true)
            .AvoidInlineMapping(true)
            .MaxDepth(2);

        if (typeof(TEntitySet).IsFromType<IEntitySetWithCreate<TKey>>())
        {
            typeConfig.Ignore(nameof(IEntitySetWithCreate<TKey>.RowVersion), nameof(IEntitySetWithCreate<TKey>.CreatedBy), nameof(IEntitySetWithCreate<TKey>.CreatedOn));
        }

        if (typeof(TEntitySet).IsFromType<IEntitySetWithCreateAndUpdate<TKey>>())
        {
            typeConfig.Ignore(nameof(IEntitySetWithCreateAndUpdate<TKey>.UpdatedBy), nameof(IEntitySetWithCreateAndUpdate<TKey>.UpdatedOn));
        }

        return config;
    }
    /// <summary>
    /// 调用实例的 Create 方法为基础字段赋值
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <param name="entitySet">业务数据</param>
    public virtual void Create<TEntitySet>(TEntitySet entitySet)
    {
        if (entitySet is IEntitySetWithCreate<TKey> entitySetWithCreate)
        {
            entitySetWithCreate.Create(GetUserName(), _appSetting.TimeKind);
        }
    }
    /// <summary>
    /// 调用实例的 Update 方法为基础字段赋值
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <param name="entitySet">业务数据</param>
    public virtual void Update<TEntitySet>(TEntitySet entitySet)
    {
        if (entitySet is IEntitySetWithCreateAndUpdate<TKey> entitySetWithCreateAndUpdate)
        {
            entitySetWithCreateAndUpdate.Update(GetUserName(), _appSetting.TimeKind);
        }
    }
    /// <summary>
    /// 从当前上下文解除当前对象的跟踪状态
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <param name="entitySet">业务数据</param>
    public virtual void Detach<TEntitySet>(TEntitySet entitySet) where TEntitySet : class, IEntitySet<TKey>
    {
        var exist = _context.Set<TEntitySet>().Local.FirstOrDefault(x => x.Id.Equals(entitySet.Id));
        if (exist != null)
        {
            _context.Entry(exist).State = EntityState.Detached;
        }
    }
    /// <summary>
    /// 逻辑修改
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    public virtual void Update<TEntitySet>(TEntitySet entitySet, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey>
    {
        if (isLogical && entitySet is IEntitySetWithCreate<TKey>)
        {
            //逻辑修改会产生一个当前数据不同主键的副本, 并设置为已更新的状态, 使用 RowVersion 字段关联更新后的数据编号
            var logicalCopy = entitySet.Copy();
            logicalCopy.Id = KeyGenerator.GetKey<TKey>();
            if (logicalCopy is IEntitySetWithCreate<TKey> entitySetWithCreate)
            {
                entitySetWithCreate.RowVersion = entitySet.Id;
                entitySetWithCreate.State = DataState.Updated;
            }

            _context.Set<TEntitySet>().Add(logicalCopy);
        }
    }
    /// <summary>
    /// 逻辑删除
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual void Delete<TEntitySet>(TEntitySet entitySet, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey>
    {
        if (isLogical && entitySet is IEntitySetWithCreate<TKey> entitySetWithCreate)
        {
            Update(entitySetWithCreate);
            entitySetWithCreate.State = DataState.Deleted;
            _context.Set<TEntitySet>().Update(entitySet);
        }
        else
        {
            _context.Set<TEntitySet>().Remove(entitySet);
        }
    }
    /// <summary>
    /// 提交当前操作
    /// </summary>
    /// <returns></returns>
    public virtual Task<int> SaveChangesAsync() => _context.SaveChangesAsync();
    /// <summary>
    /// 刷新缓存
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <returns></returns>
    public virtual async Task RefreshCacheAsync<TEntitySet>() where TEntitySet : class
    {
        var _type = typeof(TEntitySet);
        var _typeName = typeof(TEntitySet).GetDisplayName();
        var queryable = _context.Set<TEntitySet>();
        if (_enableCache && _type.HasAttribute<CacheableAttribute>())
        {
            var cacheKey = AppSettings.CACHE_DATA + _type.Name.ToUpper(CultureInfo.CurrentCulture);
            var entitySets = queryable.ToHashSet();
            await _cache.SetStringAsync(cacheKey, entitySets.ToJsonString());
            _logger.LogInformation("{typeName} has been finished refresh cache data", _typeName);
        }
    }
    #endregion

    #region 数据查询方法
    /// <summary>
    /// 非注入类型 TEntitySet 的统一数据源查询入口, 可缓存数据源, 可返回泛型类型指定的任意类型引用
    /// 缓存的前提是全局缓存开关开启且当前数据对象类型标记为可缓存
    /// </summary>
    /// <typeparam name="TEntitySet">查询的类型</typeparam>
    /// <returns></returns>
    public virtual IQueryable<TEntitySet> GetQueryable<TEntitySet>() where TEntitySet : class
    {
        var _type = typeof(TEntitySet);
        var _typeName = typeof(TEntitySet).GetDisplayName();
        var queryable = _context.Set<TEntitySet>();
        if (_enableCache && _type.HasAttribute<CacheableAttribute>())
        {
            var cacheKey = AppSettings.CACHE_DATA + _type.Name.ToUpper(CultureInfo.CurrentCulture);
            var entitySets = _cache.GetValue<IEnumerable<TEntitySet>>(cacheKey);
            if (entitySets == null)
            {
                _logger.LogInformation("{typeName} have no cache data, will query and cached", _typeName);
                entitySets = queryable.ToHashSet();
                _cache.SetValue(cacheKey, entitySets);
            }

            return entitySets.AsQueryable();
        }

        _logger.LogDebug("The user {user} query {typeName}", UserName, _typeName);
        return queryable;
    }
    /// <summary>
    /// 非注入类型 TEntitySet 的统一数据源查询入口, 可缓存数据源, 可返回泛型类型指定的任意类型引用
    /// 缓存的前提是全局缓存开关开启且当前数据对象类型标记为可缓存
    /// </summary>
    /// <typeparam name="TEntitySet">查询的类型</typeparam>
    /// <param name="predicate">过滤条件</param>
    /// <returns></returns>
    public virtual IQueryable<TEntitySet> GetQueryable<TEntitySet>(Expression<Func<TEntitySet, bool>> predicate) where TEntitySet : class => GetQueryable<TEntitySet>().Where(predicate);
    /// <summary>
    /// 根据 id 获取对象
    /// </summary>
    /// <typeparam name="TEntitySet">查询的类型</typeparam>
    /// <param name="id"></param>
    /// <returns></returns>
    public virtual async Task<TEntitySet?> GetAsync<TEntitySet>(TKey id) where TEntitySet : class, IEntitySet<TKey> => await GetQueryable<TEntitySet>().FirstOrDefaultAsync(x => x.Id.Equals(id));
    /// <summary>
    /// 从 QueryOptions 和上下文自动获取查询表达式的内容并执行查询返回分页的结果
    /// 暂不支持 orderby, select 和 expand 表达式
    /// </summary>
    /// <returns></returns>
    public virtual async Task<PagedResult> GetPagedResultAsync<TEntitySet>() where TEntitySet : class, IEntitySet<TKey>
    {
        var queryable = GetQueryable<TEntitySet>();
        var queryOptions = new QueryOptions<TEntitySet>(new QueryOptionRawValue(_httpContext.Request.Query));
        if (queryOptions.Filter != null)
        {
            if (queryOptions.Filter is NestedFilterOption<TEntitySet> filter && filter.Filters != null && filter.Filters.Any())
            {
                //目前仅支持一级嵌套, 且顶级条件仅支持 and
                //var expression = string.Join(GetFilterLogic(filter.Logic ?? LogicType.And), filter.Filters.Select(x => GetFilterExpression(x)));
                queryable = queryable.Where(string.Join(filter.GetFilterLogic(), filter.Filters.Select(x => x.GetFilterExpression())));
            }
            else if ((!string.IsNullOrWhiteSpace(queryOptions.Filter.Field)) && (!string.IsNullOrWhiteSpace(queryOptions.Filter.Value)))
            {
                queryable = queryable.Where(queryOptions.Filter.GetFilterExpression(), queryOptions.Filter.FieldValue);
            }
        }

        if (queryOptions.Orderby != null)
        {
            queryable = queryable.OrderBy(queryOptions.Orderby.ToString());
        }

        var result = new PagedResult();
        if (queryOptions.Count.HasValue && queryOptions.Count.Value)
        {
            result.Total = await queryable.CountAsync();
        }

        if (queryOptions.Skip.HasValue && queryOptions.Skip.Value > 0)
        {
            queryable = queryable.Skip(queryOptions.Skip.Value);
        }

        if (queryOptions.Top.HasValue && queryOptions.Top.Value > 0)
        {
            queryable = queryable.Take(queryOptions.Top.Value);
            result.PageSize = queryOptions.Top.Value;
        }

        result.PageIndex = (queryOptions.Skip ?? 1) / result.PageSize + 1;

        if (queryOptions.Select != null)
        {
            var columns = queryOptions.Select.ToString<string>();
            result.Rows = await queryable.Select("new {" + columns + "}", null).ToDynamicListAsync();
        }
        else
        {
            result.Rows = await queryable.ToListAsync();
        }

        return result;
    }
    /// <summary>
    /// 适用于新增时判断对象是否存在, 存在则抛出异常
    /// </summary>
    /// <typeparam name="TEntitySet">查询的类型</typeparam>
    /// <param name="id">业务数据编号</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public virtual async Task ThrowIfExistAsync<TEntitySet>(TKey? id) where TEntitySet : class, IEntitySet<TKey>
    {
        if ((!(id == null || id.Equals(default))) && await GetQueryable<TEntitySet>().AnyAsync(x => x.Id.Equals(id)))
        {
            throw new BadHttpRequestException($"{typeof(TEntitySet).GetDisplayName()} 中当前记录已存在!");
        }
    }
    /// <summary>
    /// 适用于更新时判断当前对象是否存在, 存在则返回按 Id 查询的结果, 不存在则抛出异常
    /// </summary>
    /// <typeparam name="TEntitySet">查询的类型</typeparam>
    /// <param name="id">业务数据主键</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public virtual async Task<TEntitySet> GetIfExistAsync<TEntitySet>(TKey id) where TEntitySet : class, IEntitySet<TKey>
    {
        var entitySet = await GetAsync<TEntitySet>(id);
        if (entitySet == null)
        {
            throw new BadHttpRequestException($"{typeof(TEntitySet).GetDisplayName()} 中当前记录不存在!");
        }

        return entitySet;
    }
    #endregion

    #region 数据操作方法, 只操作不提交
    /// <summary>
    /// 获取业务数据的集合成员, 成员是附加信息, 附件及特定类型的附属集合
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TElement">附加信息, 附件或附属集合的类型</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <returns></returns>
    public virtual IEnumerable<TElement>? GetCollectionMembers<TEntitySet, TElement>(TEntitySet entitySet) where TEntitySet : class, IEntitySet<TKey> where TElement : class, IEntitySet<TKey>
    {
        var propertyInfo = typeof(TEntitySet).GetProperties(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(x => x.PropertyType.IsFromType<IEnumerable<TElement>>());
        if (propertyInfo != null && propertyInfo.GetValue(entitySet) is IEnumerable<TElement> collection)
        {
            return collection;
        }

        return null;
    }
    /// <summary>
    /// 新增或修改阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作
    /// </summary>
    /// <typeparam name="TEntitySet">查询的类型</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public virtual async Task UniqueAsync<TEntitySet>(TEntitySet entitySet) where TEntitySet : class, IEntitySet<TKey>
    {
        var _type = typeof(TEntitySet);
        var propertyInfos = _type.GetProperties().Where(x => x.HasAttribute<UniqueAttribute>());
        if (propertyInfos.IsNotNullOrEmpty())
        {
            var index = 0;
            var keys = new List<string>();
            var values = new List<object>();
            var messages = new List<string>();

            if (!(entitySet.Id == null || entitySet.Id.Equals(default)))
            {
                keys.Add($"{nameof(entitySet.Id)} != @{index++}");
                values.Add($"{entitySet.Id}");
            }

            foreach (var propertyInfo in propertyInfos)
            {
                var value = propertyInfo.GetValue(entitySet);
                keys.Add($"{propertyInfo.Name} == @{index++}");
                values.Add($"{value}");
                messages.Add($"<{propertyInfo.GetDisplayName()}> = {value}");
            }

            var predicate = string.Join(" and ", keys);
            var exist = await GetQueryable<TEntitySet>().AnyAsync(predicate, values.ToArray());
            if (exist)
            {
                throw new BadHttpRequestException($"{_type.GetDisplayName()} 中 {messages.ToString<string>()} 的记录已经存在!");
            }
        }
    }
    /// <summary>
    /// 单个数据对象的新增, 自动验证重复
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> AddAsync<TEntitySet>(TEntitySet entitySet) where TEntitySet : class, IEntitySet<TKey>
    {
        await ThrowIfExistAsync<TEntitySet>(entitySet.Id);
        await UniqueAsync(entitySet);
        Create(entitySet);
        Update(entitySet);
        Detach(entitySet);
        await _context.Set<TEntitySet>().AddAsync(entitySet);
        return entitySet;
    }
    /// <summary>
    /// 单个数据对象的更新, 自动验证重复
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> UpdateAsync<TEntitySet>(TEntitySet entitySet, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey>
    {
        await UniqueAsync(entitySet);
        var entity = await GetIfExistAsync<TEntitySet>(entitySet.Id);

        //先对原数据进行逻辑更新操作
        Update(entity, isLogical);

        //修改时先查了一次当前对象, 因此不能取消附加
        //Detach(entity);
        entitySet.Adapt(entity, AdapterConfig);
        Update(entity);
        _context.Set<TEntitySet>().Update(entity);
        return entity;
    }
    /// <summary>
    /// 删除记录
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <param name="id">业务数据主键</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task DeleteAsync<TEntitySet>(TKey id, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey>
    {
        var entitySet = await GetIfExistAsync<TEntitySet>(id);
        Delete(entitySet, isLogical);
    }
    #endregion

    #region 带业务附加信息的数据操作方法
    #region 新增部分
    /// <summary>
    /// 此重载新增方法会同时将当前业务数据的附加信息一并新增
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAdditional">业务数据的附加信息 IAdditional</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> AddAsync<TEntitySet, TAdditional>(TEntitySet entitySet) where TEntitySet : class, IEntitySet<TKey> where TAdditional : class, IAdditional<TKey>
    {
        _logger.LogInformation("The user {user} will add {typeName} and it's additionals, the content is: " + entitySet, UserName, typeof(TEntitySet).GetDisplayName());

        await AddAsync(entitySet);
        await AddAdditionalsAsync<TEntitySet, TAdditional>(entitySet, default);
        await SaveChangesAsync<TEntitySet>();
        return entitySet;
    }
    /// <summary>
    /// 此重载新增方法会同时将当前业务数据的附件一并新增
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAttachment">业务数据的附件 IAttachment</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="attachments">附件</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> AddAsync<TEntitySet, TAttachment>(TEntitySet entitySet, IEnumerable<TAttachment>? attachments = default) where TEntitySet : class, IEntitySet<TKey> where TAttachment : class, IAttachment<TKey>
    {
        _logger.LogInformation("The user {user} will add {typeName} and it's attachments, the content is: " + entitySet, UserName, typeof(TEntitySet).GetDisplayName());

        await AddAsync(entitySet);
        await AddAttachmentsAsync(entitySet, attachments, default);
        await SaveChangesAsync<TEntitySet>();
        return entitySet;
    }
    /// <summary>
    /// 此重载新增方法会同时将当前类型的某一种集合成员一并新增
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TElement">集合元素数据类型, 此时的集合是 IEntitySet</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="elements">业务数据引用数据集合</param>
    /// <param name="foreignKey">外键字段名</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> AddAsync<TEntitySet, TElement>(TEntitySet entitySet, IEnumerable<TElement>? elements = default, string? foreignKey = default) where TEntitySet : class, IEntitySet<TKey> where TElement : class, IEntitySet<TKey>
    {
        _logger.LogInformation("The user {user} will add {typeName} and it's related collection elements, the content is: " + entitySet, UserName, typeof(TEntitySet).GetDisplayName());

        await AddAsync(entitySet);
        await AddCollectionAsync(entitySet, elements, foreignKey);
        await SaveChangesAsync<TEntitySet>();
        return entitySet;
    }
    /// <summary>
    /// 此重载新增方法会同时将当前业务数据的附加信息及附件一并新增
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAdditional">业务数据的附加信息 IAdditional</typeparam>
    /// <typeparam name="TAttachment">业务数据的附件 IAttachment</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="additionals">附加数据集合</param>
    /// <param name="attachments">附件</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> AddAsync<TEntitySet, TAdditional, TAttachment>(TEntitySet entitySet, IEnumerable<TAdditional>? additionals = default, IEnumerable<TAttachment>? attachments = default) where TEntitySet : class, IEntitySet<TKey> where TAdditional : class, IAdditional<TKey> where TAttachment : class, IAttachment<TKey>
    {
        _logger.LogInformation("The user {user} will add {typeName} and it's additionals, attachments, the content is: " + entitySet, UserName, typeof(TEntitySet).GetDisplayName());

        await AddAsync(entitySet);
        await AddAdditionalsAsync<TEntitySet, TAdditional>(entitySet, default);
        await AddAttachmentsAsync(entitySet, attachments, default);
        await SaveChangesAsync<TEntitySet>();
        return entitySet;
    }
    /// <summary>
    /// 此重载新增方法会同时将当前业务数据的附加信息及集合元素数据一并新增
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAdditional">业务数据的附加信息 IAdditional</typeparam>
    /// <typeparam name="TElement">业务数据的集合元素数据 IEntitySet</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="elements">业务数据的集合元素数据</param>
    /// <param name="foreignKey">外键字段名</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> AddAsync<TEntitySet, TAdditional, TElement>(TEntitySet entitySet, IEnumerable<TElement>? elements = default, string? foreignKey = default) where TEntitySet : class, IEntitySet<TKey> where TAdditional : class, IAdditional<TKey> where TElement : class, IEntitySet<TKey>
    {
        _logger.LogInformation("The user {user} will add {typeName} and it's additionals, related collection elements, the content is: " + entitySet, UserName, typeof(TEntitySet).GetDisplayName());

        await AddAsync(entitySet);
        await AddAdditionalsAsync<TEntitySet, TAdditional>(entitySet, default);
        await AddCollectionAsync(entitySet, elements, foreignKey);
        await SaveChangesAsync<TEntitySet>();
        return entitySet;
    }
    /// <summary>
    /// 此重载新增方法会同时将当前业务数据的附件及集合元素数据一并新增
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAttachment">业务数据的附件 IAttachment</typeparam>
    /// <typeparam name="TElement">业务数据的集合元素数据 IEntitySet</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="attachments">附件</param>
    /// <param name="elements">业务数据的集合元素数据</param>
    /// <param name="foreignKey">外键字段名</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> AddAsync<TEntitySet, TAttachment, TElement>(TEntitySet entitySet, IEnumerable<TAttachment>? attachments = default, IEnumerable<TElement>? elements = default, string? foreignKey = default) where TEntitySet : class, IEntitySet<TKey> where TAttachment : class, IAttachment<TKey> where TElement : class, IEntitySet<TKey>
    {
        _logger.LogInformation("The user {user} will add {typeName} and it's attachments, related collection elements, the content is: " + entitySet, UserName, typeof(TEntitySet).GetDisplayName());

        await AddAsync(entitySet);
        await AddAttachmentsAsync(entitySet, attachments, default);
        await AddCollectionAsync(entitySet, elements, foreignKey);
        await SaveChangesAsync<TEntitySet>();
        return entitySet;
    }
    /// <summary>
    /// 此重载新增方法会同时将当前业务数据的附加信息, 附件及集合元素数据一并新增
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAdditional">业务数据的附加信息 IAdditional</typeparam>
    /// <typeparam name="TAttachment">业务数据的附件 IAttachment</typeparam>
    /// <typeparam name="TElement">业务数据的集合元素数据 IEntitySet</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="additionals">附加数据集合</param>
    /// <param name="attachments">附件</param>
    /// <param name="elements">业务数据的集合元素数据</param>
    /// <param name="foreignKey">外键字段名</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> AddAsync<TEntitySet, TAdditional, TAttachment, TElement>(TEntitySet entitySet, IEnumerable<TAdditional>? additionals = default, IEnumerable<TAttachment>? attachments = default, IEnumerable<TElement>? elements = default, string? foreignKey = default) where TEntitySet : class, IEntitySet<TKey> where TAdditional : class, IAdditional<TKey> where TAttachment : class, IAttachment<TKey> where TElement : class, IEntitySet<TKey>
    {
        _logger.LogInformation("The user {user} will add {typeName} and it's additionals, attachments, related collection elements, the content is: " + entitySet, UserName, typeof(TEntitySet).GetDisplayName());

        await AddAsync(entitySet);
        await AddAdditionalsAsync<TEntitySet, TAdditional>(entitySet, default);
        await AddAttachmentsAsync(entitySet, attachments, default);
        await AddCollectionAsync(entitySet, elements, foreignKey);
        await SaveChangesAsync<TEntitySet>();
        return entitySet;
    }
    #endregion

    #region 修改部分
    /// <summary>
    /// 重载的更新方法会同时将当前类型的附加信息一并更新
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAdditional">集合元素数据类型, 此时的集合是 IAdditional</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> UpdateAsync<TEntitySet, TAdditional>(TEntitySet entitySet, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey> where TAdditional : class, IAdditional<TKey>
    {
        _logger.LogDebug("The user {user} will {isLogical} update {typeName} and it's additionals, the content is: " + entitySet, UserName, GetLogicalString(isLogical), typeof(TEntitySet).GetDisplayName());

        await UpdateAsync(entitySet, isLogical);
        await UpdateAdditionalsAsync<TEntitySet, TAdditional>(entitySet, default, isLogical);
        await SaveChangesAsync<TEntitySet>(isLogical);
        return entitySet;
    }
    /// <summary>
    /// 重载的更新方法会同时将当前业务数据的附件一并更新
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAttachment">业务数据的附件 IAttachment</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="attachments">附件</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> UpdateAsync<TEntitySet, TAttachment>(TEntitySet entitySet, IEnumerable<TAttachment>? attachments = default, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey> where TAttachment : class, IAttachment<TKey>
    {
        _logger.LogDebug("The user {user} will {isLogical} update {typeName} and it's attachments, the content is: " + entitySet, UserName, GetLogicalString(isLogical), typeof(TEntitySet).GetDisplayName());

        await UpdateAsync(entitySet, isLogical);
        var files = await UpdateAttachmentsAsync(entitySet, attachments, default, isLogical);
        await SaveChangesAndDeleteFilesAsync<TEntitySet>(isLogical, files);
        return entitySet;
    }
    /// <summary>
    /// 重载的更新方法会同时将当前业务数据引用的数据集合一并更新
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TElement">业务数据的集合元素数据 IEntitySet</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="elements">业务数据引用的数据集合</param>
    /// <param name="foreignKey">外键字段名</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> UpdateAsync<TEntitySet, TElement>(TEntitySet entitySet, IEnumerable<TElement>? elements = default, string? foreignKey = default, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey> where TElement : class, IEntitySet<TKey>
    {
        _logger.LogDebug("The user {user} will {isLogical} update {typeName} and it's related collection elements, the content is: " + entitySet, UserName, GetLogicalString(isLogical), typeof(TEntitySet).GetDisplayName());

        await UpdateAsync(entitySet, isLogical);
        await UpdateCollectionAsync(entitySet, elements, foreignKey, isLogical);
        await SaveChangesAsync<TEntitySet>(isLogical);
        return entitySet;
    }
    /// <summary>
    /// 重载的更新方法会同时将当前业务数据的附加信息及附件一并更新
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAdditional">集合元素数据类型, 此时的集合是 IAdditional</typeparam>
    /// <typeparam name="TAttachment">业务数据的附件 IAttachment</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="additionals">附加数据集合</param>
    /// <param name="attachments">附件</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> UpdateAsync<TEntitySet, TAdditional, TAttachment>(TEntitySet entitySet, IEnumerable<TAdditional>? additionals = default, IEnumerable<TAttachment>? attachments = default, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey> where TAdditional : class, IAdditional<TKey> where TAttachment : class, IAttachment<TKey>
    {
        _logger.LogDebug("The user {user} will {isLogical} update {typeName} and it's additionals, attachments, the content is: " + entitySet, UserName, GetLogicalString(isLogical), typeof(TEntitySet).GetDisplayName());

        await UpdateAsync(entitySet, isLogical);
        await UpdateAdditionalsAsync<TEntitySet, TAdditional>(entitySet, default, isLogical);
        var files = await UpdateAttachmentsAsync(entitySet, attachments, default, isLogical);
        await SaveChangesAndDeleteFilesAsync<TEntitySet>(isLogical, files);
        return entitySet;
    }
    /// <summary>
    /// 重载的更新方法会同时将当前业务数据的附加信息及业务数据引用的数据集合一并更新
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAdditional">业务数据的附加信息 IAdditional</typeparam>
    /// <typeparam name="TElement">业务数据的集合元素数据 IEntitySet</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="elements">业务数据引用的数据集合</param>
    /// <param name="foreignKey">外键字段名</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> UpdateAsync<TEntitySet, TAdditional, TElement>(TEntitySet entitySet, IEnumerable<TElement>? elements = default, string? foreignKey = default, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey> where TAdditional : class, IAdditional<TKey> where TElement : class, IEntitySet<TKey>
    {
        _logger.LogDebug("The user {user} will {isLogical} update {typeName} and it's additionals, related collection elements, the content is: " + entitySet, UserName, GetLogicalString(isLogical), typeof(TEntitySet).GetDisplayName());

        await UpdateAsync(entitySet, isLogical);
        await UpdateAdditionalsAsync<TEntitySet, TAdditional>(entitySet, default, isLogical);
        await UpdateCollectionAsync(entitySet, elements, foreignKey, isLogical);
        await SaveChangesAsync<TEntitySet>(isLogical);
        return entitySet;
    }
    /// <summary>
    /// 重载的更新方法会同时将当前业务数据的附件及业务数据引用的数据集合一并更新
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAttachment">业务数据的附件 IAttachment</typeparam>
    /// <typeparam name="TElement">业务数据的集合元素数据 IEntitySet</typeparam>
    /// <param name="entitySet"></param>
    /// <param name="attachments"></param>
    /// <param name="elements"></param>
    /// <param name="foreignKey"></param>
    /// <param name="isLogical"></param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> UpdateAsync<TEntitySet, TAttachment, TElement>(TEntitySet entitySet, IEnumerable<TAttachment>? attachments = default, IEnumerable<TElement>? elements = default, string? foreignKey = default, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey> where TAttachment : class, IAttachment<TKey> where TElement : class, IEntitySet<TKey>
    {
        _logger.LogDebug("The user {user} will {isLogical} update {typeName} and it's attachments, related collection elements, the content is: " + entitySet, UserName, GetLogicalString(isLogical), typeof(TEntitySet).GetDisplayName());

        await UpdateAsync(entitySet, isLogical);
        var files = await UpdateAttachmentsAsync(entitySet, attachments, default, isLogical);
        await UpdateCollectionAsync(entitySet, elements, foreignKey, isLogical);
        await SaveChangesAndDeleteFilesAsync<TEntitySet>(isLogical, files);
        return entitySet;
    }
    /// <summary>
    /// 重载的更新方法会同时将当前业务数据的附加信息, 附件及业务数据引用的数据集合一并更新
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAdditional">业务数据的附加信息 IAdditional</typeparam>
    /// <typeparam name="TAttachment">业务数据的附件 IAttachment</typeparam>
    /// <typeparam name="TElement">业务数据的集合元素数据 IEntitySet</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="additionals">附加数据集合</param>
    /// <param name="attachments">附件</param>
    /// <param name="elements">业务数据引用的数据集合</param>
    /// <param name="foreignKey">外键字段名</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> UpdateAsync<TEntitySet, TAdditional, TAttachment, TElement>(TEntitySet entitySet, IEnumerable<TAdditional>? additionals = default, IEnumerable<TAttachment>? attachments = default, IEnumerable<TElement>? elements = default, string? foreignKey = default, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey> where TAdditional : class, IAdditional<TKey> where TAttachment : class, IAttachment<TKey> where TElement : class, IEntitySet<TKey>
    {
        _logger.LogDebug("The user {user} will {isLogical} update {typeName} and it's additionals, attachments, related collection elements, the content is: " + entitySet, UserName, GetLogicalString(isLogical), typeof(TEntitySet).GetDisplayName());

        await UpdateAsync(entitySet, isLogical);
        await UpdateAdditionalsAsync<TEntitySet, TAdditional>(entitySet, default, isLogical);
        var files = await UpdateAttachmentsAsync(entitySet, attachments, default, isLogical);
        await UpdateCollectionAsync(entitySet, elements, foreignKey, isLogical);
        await SaveChangesAndDeleteFilesAsync<TEntitySet>(isLogical, files);
        return entitySet;
    }
    #endregion

    #region 删除部分
    /// <summary>
    /// 重载的删除方法会同时将当前业务数据的附加信息一并删除
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAdditional">集合元素数据类型, 此时的集合是 IAdditional</typeparam>
    /// <param name="additionalId">业务数据主键</param>
    /// <param name="additionals">附加数据集合</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task DeleteAsync<TEntitySet, TAdditional>(TKey additionalId, IEnumerable<TAdditional>? additionals = default, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey> where TAdditional : class, IAdditional<TKey>
    {
        _logger.LogDebug("The user {user} will {isLogical} delete {typeName} and it's additionals, the content id is: " + additionalId, UserName, GetLogicalString(isLogical), typeof(TEntitySet).GetDisplayName());

        await DeleteAdditionalsAsync(additionalId, additionals, isLogical);
        await DeleteAsync<TEntitySet>(additionalId, isLogical);
        await SaveChangesAsync<TEntitySet>(isLogical);
    }
    /// <summary>
    /// 重载的删除方法会同时将当前业务数据的附件一并删除
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAttachment">业务数据的附件 IAttachment</typeparam>
    /// <param name="businessKey">业务主键</param>
    /// <param name="businessType">业务类型</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task DeleteAsync<TEntitySet, TAttachment>(TKey businessKey, string? businessType = default, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey> where TAttachment : class, IAttachment<TKey>
    {
        _logger.LogDebug("The user {user} will {isLogical} delete {typeName} and it's attachments, the content id is: " + businessKey, UserName, GetLogicalString(isLogical), typeof(TEntitySet).GetDisplayName());

        var files = await DeleteAttachmentsAsync<TAttachment>(businessKey, businessType, isLogical);
        await DeleteAsync<TEntitySet>(businessKey, isLogical);
        await SaveChangesAndDeleteFilesAsync<TEntitySet>(isLogical, files);
    }
    /// <summary>
    /// 重载的删除方法会同时将当前业务数据引用的数据集合一并删除
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TElement">业务数据的集合元素数据 IEntitySet</typeparam>
    /// <param name="id">业务数据主键</param>
    /// <param name="elements">业务数据引用的数据集合</param>
    /// <param name="foreignKey">外键字段名</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task DeleteAsync<TEntitySet, TElement>(TKey id, IEnumerable<TElement>? elements = default, string? foreignKey = default, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey> where TElement : class, IEntitySet<TKey>
    {
        _logger.LogDebug("The user {user} will {isLogical} delete {typeName} and it's related collection elements, the content id is: " + id, UserName, GetLogicalString(isLogical), typeof(TEntitySet).GetDisplayName());

        await DeleteCollectionAsync<TEntitySet, TElement>(id, elements, foreignKey, isLogical);
        await DeleteAsync<TEntitySet>(id, isLogical);
        await SaveChangesAsync<TEntitySet>(isLogical);
    }
    /// <summary>
    /// 重载的删除方法会同时将当前业务数据的附加信息及附件一并删除
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAdditional">业务数据的附加信息 IAdditional</typeparam>
    /// <typeparam name="TAttachment">业务数据的附件 IAttachment</typeparam>
    /// <param name="id">业务数据主键</param>
    /// <param name="additionals">附加数据集合</param>
    /// <param name="businessType">业务类型</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task DeleteAsync<TEntitySet, TAdditional, TAttachment>(TKey id, IEnumerable<TAdditional>? additionals = default, string? businessType = default, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey> where TAdditional : class, IAdditional<TKey> where TAttachment : class, IAttachment<TKey>
    {
        _logger.LogDebug("The user {user} will {isLogical} delete {typeName} and it's additionals, attachments, the content id is: " + id, UserName, GetLogicalString(isLogical), typeof(TEntitySet).GetDisplayName());

        await DeleteAdditionalsAsync<TAdditional>(id, default, isLogical);
        var files = await DeleteAttachmentsAsync<TAttachment>(id, businessType, isLogical);
        await DeleteAsync<TEntitySet>(id, isLogical);
        await SaveChangesAndDeleteFilesAsync<TEntitySet>(isLogical, files);
    }
    /// <summary>
    /// 重载的删除方法会同时将当前业务数据的附加信息及业务数据引用的数据集合一并删除
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAdditional">业务数据的附加信息 IAdditional</typeparam>
    /// <typeparam name="TElement">业务数据的集合元素数据 IEntitySet</typeparam>
    /// <param name="id">业务数据主键</param>
    /// <param name="additionals">附加数据集合</param>
    /// <param name="elements">业务数据引用的数据集合</param>
    /// <param name="foreignKey">外键字段名</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task DeleteAsync<TEntitySet, TAdditional, TElement>(TKey id, IEnumerable<TAdditional>? additionals = default, IEnumerable<TElement>? elements = default, string? foreignKey = default, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey> where TAdditional : class, IAdditional<TKey> where TElement : class, IEntitySet<TKey>
    {
        _logger.LogDebug("The user {user} will {isLogical} delete {typeName} and it's additionals, related collection elements, the content id is: " + id, UserName, GetLogicalString(isLogical), typeof(TEntitySet).GetDisplayName());

        await DeleteAdditionalsAsync<TAdditional>(id, default, isLogical);
        await DeleteCollectionAsync<TElement, TElement>(id, elements, foreignKey, isLogical);
        await DeleteAsync<TEntitySet>(id, isLogical);
        await SaveChangesAsync<TEntitySet>(isLogical);
    }
    /// <summary>
    /// 重载的删除方法会同时将当前业务数据的附件及业务数据引用的数据集合一并删除
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAttachment">业务数据的附件 IAttachment</typeparam>
    /// <typeparam name="TElement">业务数据的集合元素数据 IEntitySet</typeparam>
    /// <param name="id">业务数据主键</param>
    /// <param name="businessType">业务类型</param>
    /// <param name="elements">业务数据引用的数据集合</param>
    /// <param name="foreignKey">外键字段名</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task DeleteAsync<TEntitySet, TAttachment, TElement>(TKey id, string? businessType = default, IEnumerable<TElement>? elements = default, string? foreignKey = default, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey> where TAttachment : class, IAttachment<TKey> where TElement : class, IEntitySet<TKey>
    {
        _logger.LogDebug("The user {user} will {isLogical} delete {typeName} and it's attachments, related collection elements, the content id is: " + id, UserName, GetLogicalString(isLogical), typeof(TEntitySet).GetDisplayName());

        var files = await DeleteAttachmentsAsync<TAttachment>(id, businessType, isLogical);
        await DeleteCollectionAsync<TEntitySet, TElement>(id, elements, foreignKey, isLogical);
        await DeleteAsync<TEntitySet>(id, isLogical);
        await SaveChangesAndDeleteFilesAsync<TEntitySet>(isLogical, files);
    }
    /// <summary>
    /// 重载的删除方法会同时将当前业务数据的附加信息, 附件及业务数据引用的数据集合一并删除
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAdditional">业务数据的附加信息 IAdditional</typeparam>
    /// <typeparam name="TAttachment">业务数据的附件 IAttachment</typeparam>
    /// <typeparam name="TElement">业务数据的集合元素数据 IEntitySet</typeparam>
    /// <param name="id">业务数据主键</param>
    /// <param name="additionals">附加数据集合</param>
    /// <param name="businessType">业务类型</param>
    /// <param name="elements">业务数据引用的数据集合</param>
    /// <param name="foreignKey">外键字段名</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task DeleteAsync<TEntitySet, TAdditional, TAttachment, TElement>(TKey id, IEnumerable<TAdditional>? additionals = default, string? businessType = default, IEnumerable<TElement>? elements = default, string? foreignKey = default, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey> where TAdditional : class, IAdditional<TKey> where TAttachment : class, IAttachment<TKey> where TElement : class, IEntitySet<TKey>
    {
        _logger.LogDebug("The user {user} will {isLogical} delete {typeName} and it's additionals, attachments, related collection elements, the content id is: " + id, UserName, GetLogicalString(isLogical), typeof(TEntitySet).GetDisplayName());

        await DeleteAdditionalsAsync<TAdditional>(id, default, isLogical);
        var files = await DeleteAttachmentsAsync<TAttachment>(id, businessType, isLogical);
        await DeleteCollectionAsync<TEntitySet, TElement>(id, elements, foreignKey, isLogical);
        await DeleteAsync<TEntitySet>(id, isLogical);
        await SaveChangesAndDeleteFilesAsync<TEntitySet>(isLogical, files);
    }
    #endregion    
    #endregion

    #region 业务附加信息类操作方法
    /// <summary>
    /// 新增业务数据的附加信息
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAdditional">集合元素数据类型, 此时的集合是 IAdditional</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="additionals">附加数据集合</param>
    /// <returns></returns>
    public virtual async Task AddAdditionalsAsync<TEntitySet, TAdditional>(TEntitySet entitySet, IEnumerable<TAdditional>? additionals = default) where TEntitySet : class, IEntitySet<TKey> where TAdditional : class, IAdditional<TKey>
    {
        additionals ??= GetCollectionMembers<TEntitySet, TAdditional>(entitySet);
        if (additionals != null && additionals.Any())
        {
            var dbSetAdditional = _context.Set<TAdditional>();
            foreach (var additional in additionals)
            {
                additional.AdditionalId = entitySet.Id;
                Create(additional);
                Detach(additional);
                await dbSetAdditional.AddAsync(additional);
            }
        }
    }
    /// <summary>
    /// 更新业务数据的附加信息
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TAdditional">集合元素数据类型, 此时的集合是 IAdditional</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="additionals">附加数据集合</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task UpdateAdditionalsAsync<TEntitySet, TAdditional>(TEntitySet entitySet, IEnumerable<TAdditional>? additionals = default, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey> where TAdditional : class, IAdditional<TKey>
    {
        var dbSetAdditional = _context.Set<TAdditional>();
        var news = additionals ??= GetCollectionMembers<TEntitySet, TAdditional>(entitySet) ?? new List<TAdditional>();
        var olds = await GetQueryable<TAdditional>().Where(x => x.AdditionalId != null && x.AdditionalId.Equals(entitySet.Id)).ToListAsync() ?? new List<TAdditional>();
        foreach (var entity in news)
        {
            var old = olds.FirstOrDefault(x => x.Id != null && x.Id.Equals(entity.Id));
            if (old == null)
            {
                //新的有旧的没有, 则新增
                entity.AdditionalId = entitySet.Id;
                Create(entity);
                Detach(entity);
                dbSetAdditional.Add(entity);
            }
            else
            {
                //新的有旧的也有, 则更新
                //先对原数据进行逻辑更新
                Update(old, isLogical);
                old.Index = entity.Index;
                old.Key = entity.Key;
                old.Value = entity.Value;
                Update(old);
                dbSetAdditional.Update(old);
            }
        }

        foreach (var old in olds)
        {
            if (!news.Any(x => x.Id != null && x.Id.Equals(old.Id)))
            {
                //旧的有新的没有, 则删除
                //对原数据进行逻辑更新
                Delete(old, isLogical);
            }
        }
    }
    /// <summary>
    /// 删除业务数据的附加信息
    /// </summary>
    /// <typeparam name="TAdditional">附加信息数据类型</typeparam>
    /// <param name="additionalId">业务数据主键</param>
    /// <param name="additionals">附加数据集合</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task DeleteAdditionalsAsync<TAdditional>(TKey additionalId, IEnumerable<TAdditional>? additionals = default, bool isLogical = false) where TAdditional : class, IAdditional<TKey>
    {
        additionals ??= await GetQueryable<TAdditional>().Where(x => x.AdditionalId != null && x.AdditionalId.Equals(additionalId)).ToListAsync();
        if (additionals == null || additionals.Any())
        {
            return;
        }

        foreach (var additional in additionals)
        {
            Delete(additional, isLogical);
        }
    }
    #endregion

    #region 业务数据引用关系的集合类操作方法
    /// <summary>
    /// 新增业务数据对象中 TElement 类型的集合对象
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TElement">集合元素数据类型</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="elements">引用数据集合</param>
    /// <param name="foreignKey">外键字段名</param>
    /// <returns></returns>
    public virtual async Task AddCollectionAsync<TEntitySet, TElement>(TEntitySet entitySet, IEnumerable<TElement>? elements = default, string? foreignKey = default) where TEntitySet : class, IEntitySet<TKey> where TElement : class, IEntitySet<TKey>
    {
        elements ??= GetCollectionMembers<TEntitySet, TElement>(entitySet);
        if (elements == null || !elements.Any())
        {
            return;
        }

        foreignKey ??= GetForeignKey<TEntitySet, TElement>(foreignKey);
        if (string.IsNullOrWhiteSpace(foreignKey))
        {
            return;
        }

        var propertyInfo = typeof(TElement).GetProperty(foreignKey);
        if (propertyInfo == null)
        {
            return;
        }

        var dbSetElement = _context.Set<TElement>();
        foreach (var element in elements)
        {
            Create(element);
            Update(element);
            Detach(element);
            propertyInfo.SetValue(element, entitySet.Id);
            await dbSetElement.AddAsync(element);
        }
    }
    /// <summary>
    /// 更新业务数据对象中 TElement 类型的集合对象
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TElement">集合元素数据类型</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="elements">引用数据集合</param>
    /// <param name="foreignKey">外键字段名</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task UpdateCollectionAsync<TEntitySet, TElement>(TEntitySet entitySet, IEnumerable<TElement>? elements = default, string? foreignKey = default, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey> where TElement : class, IEntitySet<TKey>
    {
        elements ??= GetCollectionMembers<TEntitySet, TElement>(entitySet);
        if (elements == null || !elements.Any())
        {
            return;
        }

        foreignKey ??= GetForeignKey<TEntitySet, TElement>(foreignKey);
        if (string.IsNullOrWhiteSpace(foreignKey))
        {
            return;
        }

        var propertyInfo = typeof(TElement).GetProperty(foreignKey);
        if (propertyInfo == null)
        {
            return;
        }

        var dbSetElement = _context.Set<TElement>();
        var adapterConfig = GetAdapterConfig<TElement>();
        var olds = await GetQueryable<TElement>().Where($"{foreignKey} == @0", entitySet.Id).ToListAsync();
        foreach (var entity in elements)
        {
            var old = olds.FirstOrDefault(x => x.Id.Equals(entity.Id));
            if (old == null)
            {
                //新的有旧的没有, 则新增
                Create(entity);
                Update(entity);
                Detach(entity);
                propertyInfo.SetValue(entity, entitySet.Id);
                await dbSetElement.AddAsync(entity);
            }
            else
            {
                //新的有旧的也有, 则更新
                //先对原数据进行逻辑更新
                Update(old, isLogical);
                entity.Adapt(old, adapterConfig);
                Update(old);
                dbSetElement.Update(old);
            }
        }

        foreach (var old in olds)
        {
            if (!elements.Any(x => x.Id.Equals(old.Id)))
            {
                //旧的有新的没有, 则删除
                Delete(old, isLogical);
            }
        }
    }
    /// <summary>
    /// 删除业务数据对象中 TElement 类型的集合对象
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TElement">集合元素数据类型</typeparam>
    /// <param name="id">业务数据主键</param>
    /// <param name="elements">引用数据集合</param>
    /// <param name="foreignKey">外键字段名</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task DeleteCollectionAsync<TEntitySet, TElement>(TKey id, IEnumerable<TElement>? elements = default, string? foreignKey = default, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey> where TElement : class, IEntitySet<TKey>
    {
        foreignKey ??= GetForeignKey<TEntitySet, TElement>(foreignKey);
        if (string.IsNullOrWhiteSpace(foreignKey))
        {
            return;
        }

        elements ??= await GetQueryable<TElement>().Where($"{foreignKey} == @0", id).ToListAsync();
        if (elements == null || elements.Any())
        {
            return;
        }

        foreach (var element in elements)
        {
            Delete(element, isLogical);
        }
    }
    #endregion

    #region 文件及业务数据附件操作方法
    /// <summary>
    /// 获取并返回文件名指向的物理文件
    /// </summary>
    /// <param name="fileName">文件名</param>
    /// <returns></returns>
    public virtual IActionResult GetFile(string fileName)
    {
        var filePath = GetAttachmentPath(fileName);
        if (!File.Exists(filePath))
        {
            _logger.LogError($"File {fileName} not found!");
            return new NotFoundResult();
        }

        _logger.LogInformation("User {user} get the file from path " + $"<{filePath}> and name <{fileName}> success", UserName);
        return new PhysicalFileResult(filePath, ContentType.Default) { FileDownloadName = Path.GetFileName(fileName) };
    }
    /// <summary>
    /// 文件下载, 提供类似于文件夹目录结构的访问方式进行下载
    /// 文件路径只支持一级路径, 适用于访问自动保存的文件或者符合目录规范的文件
    /// 形如 2020-01-01/xxx.jpg
    /// </summary>
    /// <typeparam name="TAttachment">附件</typeparam>
    /// <param name="fileName">文件名</param>
    /// <param name="filePath">文件路径(只支持一级路径)</param>
    /// <returns></returns>
    public virtual async Task<TAttachment?> GetAttachmentAsync<TAttachment>(string? fileName, string? filePath = default) where TAttachment : class, IAttachment<TKey>
    {
        fileName ??= _request.GetRequestOption<string>(nameof(fileName));
        filePath ??= _request.GetRequestOption<string>(nameof(filePath));
        if (string.IsNullOrWhiteSpace(fileName))
        {
            _logger.LogError("缺少必须的文件名!");
            throw new ArgumentNullException(fileName, "缺少必须的文件名!");
        }

        _logger.LogInformation("User {user} get the file from " + $"path <{filePath}> and name <{fileName}>", UserName);
        filePath = string.IsNullOrWhiteSpace(filePath) ? fileName : Path.Combine(filePath, fileName);
        return await GetQueryable<TAttachment>().FirstOrDefaultAsync(x => x.Path == filePath).ConfigureAwait(false);
    }
    /// <summary>
    /// 获取附件对应的文件, 适用于文件下载
    /// </summary>
    /// <typeparam name="TAttachment">附件</typeparam>
    /// <param name="fileName"></param>
    /// <param name="filePath"></param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public virtual async Task<IActionResult> GetAttachmentFileAsync<TAttachment>(string? fileName, string? filePath = default) where TAttachment : class, IAttachment<TKey>
    {
        var attachment = await GetAttachmentAsync<TAttachment>(fileName, filePath);
        if (attachment == null || string.IsNullOrWhiteSpace(attachment.Path))
        {
            _logger.LogError($"File {filePath}/{fileName} not found!");
            return new NotFoundResult();
        }

        filePath = GetAttachmentPath(attachment.Path);
        if (!File.Exists(filePath))
        {
            _logger.LogError($"File {filePath}/{fileName} not found!");
            return new NotFoundResult();
        }

        _logger.LogInformation("User {user} get the file from " + $"path <{filePath}> and name <{fileName}>", UserName);
        return new PhysicalFileResult(filePath, attachment.ContentType ?? ContentType.Default) { FileDownloadName = attachment.Name ?? fileName };
    }
    /// <summary>
    /// 获取文件, 按业务类型和业务主键获取
    /// </summary>
    /// <typeparam name="TAttachment">附件</typeparam>
    /// <param name="businessType">业务类型</param>
    /// <param name="businessKey">业务主键</param>
    /// <returns></returns>
    public virtual async Task<IEnumerable<TAttachment>?> GetAttachmentsAsync<TAttachment>(string? businessType = default, TKey? businessKey = default) where TAttachment : class, IAttachment<TKey>
    {
        businessType ??= _request.GetRequestOption<string>(nameof(businessType));
        businessKey ??= _request.GetRequestOption<TKey>(nameof(businessKey));
        _logger.LogInformation("User {user} query the attachment list with businessType {businessType} " + $"and businessKey {businessKey}", UserName, businessType);
        if (string.IsNullOrWhiteSpace(businessType) || businessKey == null || businessKey.Equals(default))
        {
            throw new BadHttpRequestException("必须提供业务相关参数!");
        }

        var queryable = GetQueryable<TAttachment>();
        if (!string.IsNullOrWhiteSpace(businessType))
        {
            queryable = queryable.Where(x => x.BusinessType == businessType);
        }

        if (!(businessKey == null || businessKey.Equals(default)))
        {
            queryable = queryable.Where(x => x.BusinessKey != null && x.BusinessKey.Equals(businessKey));
        }

        var attachments = await queryable.ToListAsync();
        _logger.LogInformation("User {user} query the attachment list with businessType {businessType} " + $"and businessKey {businessKey} success", UserName, businessType);
        return attachments;
    }

    /// <summary>
    /// 单个文件上传
    /// </summary>
    /// <typeparam name="TAttachment">附件</typeparam>
    /// <param name="file"></param>
    /// <returns></returns>
    public virtual async Task<UploadResult<TAttachment, TKey>> UploadAsync<TAttachment>(IFormFile? file = default) where TAttachment : class, IAttachment<TKey>, new()
    {
        if (file == null && _request.HasFormContentType && _request.Form.Files.Any())
        {
            file = _request.Form.Files[0];
        }

        if (file == null)
        {
            throw new BadHttpRequestException("没有提交任何文件!");
        }

        var uploadResult = new UploadResult<TAttachment, TKey>
        {
            FileName = file.FileName,
            StatusCode = HttpStatusCode.NotAcceptable
        };

        if (file.Length <= 0)
        {
            uploadResult.Result = "文件内容为空!";
            return uploadResult;
        }

        if (file.Length > MaxUploadFileSize)
        {
            uploadResult.Result = $"文件 {file.Name} 大小超出 {MaxUploadFileSize} 单文件最大上传限制!";
            return uploadResult;
        }

        var ext = Path.GetExtension(file.FileName);
        var extUpper = ext.ToUpperInvariant();
        if (!AllowFileExtensions.Any(x => x == extUpper))
        {
            uploadResult.Result = "文件类型禁止上传!";
            return uploadResult;
        }

        var attachment = new TAttachment()
        {
            Name = file.FileName,
            Length = file.Length,
            Extension = ext,
            ContentType = file.ContentType
        };

        var attachmentPath = DateTime.UtcNow.ToString(AppSettings.DEFAULT_DATE_FORMAT, CultureInfo.InvariantCulture);
        var attachmentFile = GetAttachmentPath(attachmentPath);
        if (!Directory.Exists(attachmentFile))
        {
            Directory.CreateDirectory(attachmentFile);
        }

        var fileName = KeyGenerator.GetKey() + ext;
        attachment.Path = Path.Combine(attachmentPath, fileName);
        fileName = Path.Combine(attachmentFile, fileName);

        try
        {
            using (var stream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Read))
            {
                await file.CopyToAsync(stream).ConfigureAwait(false);
                stream.Close();
            }

            uploadResult.StatusCode = HttpStatusCode.OK;
            uploadResult.Result = attachment.Path;
            uploadResult.Attachment = attachment;

            _logger.LogInformation("User {user} upload the " + $"file <{uploadResult.FileName}> success with result <{uploadResult.Result}>!", UserName);

            #region 图片文件创建缩略图
            if ((_appSetting.Attachment?.EnableImageThumbnail ?? false) && attachment.ContentType.ToUpperInvariant().StartsWith(nameof(SixLabors.ImageSharp.Image).ToUpperInvariant(), StringComparison.InvariantCultureIgnoreCase))
            {
                await fileName.CreateThumbnailAsync(ImageThumbnailSize, ImageThumbnailSize, Path.Combine(attachmentFile, ImageThumbnailPrefix + Path.GetFileName(fileName)));
                _logger.LogInformation("User {user} upload the " + $"file <{uploadResult.FileName}> and create image thumbnail success with result <{uploadResult.Result}>!", UserName);
            }
            #endregion
        }
        catch (Exception ex)
        {
            uploadResult.StatusCode = HttpStatusCode.NotAcceptable;
            uploadResult.Result = ex.Message;
            _logger.LogError(ex, "User {user} upload the " + $"file <{uploadResult.FileName}> fail with error: <{ex.GetMessage()}>!", UserName);
        }

        return uploadResult;
    }
    /// <summary>
    /// 批量文件上传
    /// </summary>
    /// <typeparam name="TAttachment">附件</typeparam>
    /// <param name="files"></param>
    /// <returns></returns>
    public virtual async Task<IEnumerable<UploadResult<TAttachment, TKey>>> UploadAsync<TAttachment>(IEnumerable<IFormFile>? files = default) where TAttachment : class, IAttachment<TKey>, new()
    {
        if ((files == null || (!files.Any())) && _request.HasFormContentType && _request.Form.Files.IsNotNullOrEmpty())
        {
            files = _request.Form.Files;
        }

        if (files == null || (!files.Any()) || files.Sum(x => x.Length) <= 0)
        {
            throw new BadHttpRequestException("没有提交任何文件!");
        }

        if (files.Sum(x => x.Length) > TotalUploadFileSize)
        {
            throw new BadHttpRequestException($"文件总大小超出 {TotalUploadFileSize} 最大上传限制!");
        }

        var filePath = DateTime.UtcNow.ToString(AppSettings.DEFAULT_DATE_FORMAT, CultureInfo.InvariantCulture);
        var attachmentPath = GetAttachmentPath(filePath);
        if (!Directory.Exists(attachmentPath))
        {
            Directory.CreateDirectory(attachmentPath);
        }

        var uploadResults = new List<UploadResult<TAttachment, TKey>>();
        var allowFileExtensions = (_appSetting.Attachment?.AllowFileExtensions ?? AppSettings.DEFAULT_ALLOW_FILE_EXTENSIONS).Split(AppSettings.CHAR_COMMA, StringSplitOptions.RemoveEmptyEntries);
        foreach (var file in files)
        {
            uploadResults.Add(await UploadAsync<TAttachment>(file));
        }

        if (uploadResults.Count > 0)
        {
            //日志记录本次上传最终结果: 任意一个文件上传成功算成功
            var fileNames = uploadResults.Select(x => x.FileName ?? string.Empty).ToString<string>();
            var results = uploadResults.Select(x => x.Result ?? string.Empty).ToString<string>();
            if (uploadResults.Any(x => x.StatusCode == HttpStatusCode.OK))
            {
                _logger.LogInformation("User {user} upload " + $"the files <{fileNames}> success with result <{results}>!", UserName);
            }
            else
            {
                _logger.LogWarning("User {user} upload " + $"the files <{fileNames}> success with result <{results}>!", UserName);
            }
        }

        return uploadResults;
    }

    /// <summary>
    /// 新增附件
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="attachments">附件集合</param>
    /// <param name="businessType">业务类型</param>
    /// <returns>待删除文件列表, 请在数据库事物提交成功后删除列表中的文件</returns>
    public virtual async Task AddAttachmentsAsync<TEntitySet, TAttachment>(TEntitySet entitySet, IEnumerable<TAttachment>? attachments = default, string? businessType = default) where TEntitySet : class, IEntitySet<TKey> where TAttachment : class, IAttachment<TKey>
    {
        attachments ??= GetCollectionMembers<TEntitySet, TAttachment>(entitySet);
        if (attachments == null || !attachments.Any())
        {
            return;
        }

        businessType ??= GetBusinessType<TEntitySet>();
        var _dbSetAttachment = _context.Set<TAttachment>();
        foreach (var attachment in attachments)
        {
            attachment.BusinessKey ??= entitySet.Id;
            attachment.BusinessType ??= businessType;
            Create(attachment);
            Detach(attachment);
            await _dbSetAttachment.AddAsync(attachment);
        }

        SetBusinessAttachmentPath(entitySet, attachments);
    }
    /// <summary>
    /// 修改附件, 保留旧附件, 新增新附件, 返回待删除文件列表
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="attachments">附件集合</param>
    /// <param name="businessType">业务类型</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns>待删除文件列表, 请在数据库事物提交成功后删除列表中的文件</returns>
    public virtual async Task<IEnumerable<string>> UpdateAttachmentsAsync<TEntitySet, TAttachment>(TEntitySet entitySet, IEnumerable<TAttachment>? attachments = default, string? businessType = default, bool isLogical = false) where TEntitySet : class, IEntitySet<TKey> where TAttachment : class, IAttachment<TKey>
    {
        businessType ??= GetBusinessType<TEntitySet>();
        var _dbSetAttachment = _context.Set<TAttachment>();
        var news = attachments ?? GetCollectionMembers<TEntitySet, TAttachment>(entitySet) ?? new List<TAttachment>();
        var olds = await GetQueryable<TAttachment>().Where(x => x.BusinessType == businessType && x.BusinessKey != null && x.BusinessKey.Equals(entitySet.Id)).ToListAsync() ?? new List<TAttachment>();
        foreach (var attachment in news)
        {
            if (!olds.Any(x => x.Path == attachment.Path || x.Id.Equals(attachment.Id)))
            {
                //新的有旧的没有, 则新增, 附件只有新增和删除, 没有修改的说法
                attachment.BusinessKey ??= entitySet.Id;
                attachment.BusinessType ??= businessType;
                Create(attachment);
                Detach(attachment);
                _dbSetAttachment.Add(attachment);
            }
        }

        var files = new List<string>();
        foreach (var attachment in olds)
        {
            //旧的有新的没有则删除
            //找 Id 或者 Path 值相等的, 新的里面找不到则删除旧的
            if ((!string.IsNullOrWhiteSpace(attachment.Path)) && (!news.Any(x => x.Id.Equals(attachment.Id) || x.Path == attachment.Path)))
            {
                files.Add(attachment.Path);
                Delete(attachment, isLogical);
            }
        }

        SetBusinessAttachmentPath(entitySet, attachments);
        return files;
    }
    /// <summary>
    /// 自动删除附件, 返回待删除文件列表, 适用于实体对象模型从数据库删除时查找原始附件并自动删除
    /// </summary>
    /// <param name="businessKey">业务主键</param>
    /// <param name="businessType">业务类型</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns>待删除文件列表, 请在数据库事物提交成功后删除列表中的文件</returns>
    public virtual async Task<IEnumerable<string>?> DeleteAttachmentsAsync<TAttachment>(TKey businessKey, string? businessType = default, bool isLogical = false) where TAttachment : class, IAttachment<TKey>
    {
        var queryable = GetQueryable<TAttachment>();
        if (businessKey != null && !businessKey.Equals(default))
        {
            queryable = queryable.Where(x => x.BusinessKey != null && x.BusinessKey.Equals(businessKey));
        }

        if (!string.IsNullOrWhiteSpace(businessType))
        {
            queryable = queryable.Where(x => x.BusinessType == businessType);
        }

        var attachments = await queryable.ToListAsync();
        if (attachments.Count < 1)
        {
            return null;
        }

        var files = attachments.Select(x => x.Path ?? string.Empty).Distinct();
        foreach (var attachment in attachments)
        {
            Delete(attachment, isLogical);
        }

        return files;
    }
    /// <summary>
    /// 按文件列表删除附件
    /// </summary>
    /// <param name="fileNames">待删除的文件名</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task<IEnumerable<string>?> DeleteAttachmentsAsync<TAttachment>(IEnumerable<string> fileNames, bool isLogical = false) where TAttachment : class, IAttachment<TKey>
    {
        var attachments = await GetQueryable<TAttachment>().Where(x => x.Path != null && fileNames.Contains(x.Path)).ToListAsync();
        if (attachments.Count < 1)
        {
            return null;
        }

        var files = attachments.Select(x => x.Path ?? string.Empty).Distinct();
        foreach (var attachment in attachments)
        {
            Delete(attachment, isLogical);
        }

        return files;
    }

    /// <summary>
    /// 删除附件物理文件, 请在附件相关操作的事物提交之后执行此方法
    /// </summary>
    /// <param name="files">待删除的文件</param>
    public virtual void Delete(IEnumerable<string> files)
    {
        if (!files.Any())
        {
            return;
        }

        foreach (var file in files)
        {
            try
            {
                _logger.LogInformation("The {user} will delete the file: " + file, UserName);
                var filePath = GetAttachmentPath(file);
                if (File.Exists(filePath))
                {
                    _logger.LogDebug($"The file {file} exist, then will delete");
                    File.Delete(filePath);
                }

                //如果存在文件缩略图则删除
                var dirPath = Path.GetDirectoryName(filePath);
                if (Directory.Exists(dirPath))
                {
                    var newFileName = Path.Combine(dirPath, ImageThumbnailPrefix + Path.GetFileName(filePath));
                    if (File.Exists(newFileName))
                    {
                        _logger.LogDebug($"the file {file} thumbnail exist, then will delete file thumbnail");
                        File.Delete(newFileName);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"file {file} delete throw exception");
            }
        }

        _logger.LogInformation("User {user} delete the files " + $"<{files.ToString<string>()}> from file system success", UserName);
    }
    #endregion

    #region 附件相关基础属性
    /// <summary>
    /// 允许上传的文件类型
    /// </summary>
    protected string[] AllowFileExtensions => (_appSetting.Attachment?.AllowFileExtensions ?? AppSettings.DEFAULT_ALLOW_FILE_EXTENSIONS).Split(AppSettings.CHAR_COMMA, StringSplitOptions.RemoveEmptyEntries);
    /// <summary>
    /// 附件保存时按当前时间产生的路径
    /// </summary>
    protected string AttachmentPath => DateTime.UtcNow.ToString(AppSettings.DEFAULT_DATE_FORMAT, CultureInfo.InvariantCulture);
    /// <summary>
    /// 图片文件缩略图前缀
    /// </summary>
    protected string ImageThumbnailPrefix => _appSetting.Attachment?.ImageThumbnailPrefix ?? AppSettings.DEFAULT_IMAGE_THUMBNAIL_PREFIX;
    /// <summary>
    /// 图片文件缩略图尺寸
    /// </summary>
    protected int ImageThumbnailSize => _appSetting.Attachment?.ImageThumbnailSize ?? AppSettings.DEFAULT_IMAGE_THUMBNAIL_SIZE;
    /// <summary>
    /// 附件保存时按当前时间产生的绝对路径
    /// </summary>
    protected string AttachmentFilePath
    {
        get
        {
            var path = GetAttachmentPath(AttachmentPath);
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }

            return path;
        }
    }
    /// <summary>
    /// 单个文件最大允许上传的大小
    /// </summary>
    protected int MaxUploadFileSize
    {
        get
        {
            var fileSize = _appSetting.Attachment?.MaxUploadFileSize ?? AppSettings.DEFAULT_MAX_UPLOAD_FILE_SIZE;
            if (fileSize <= 0)
            {
                fileSize = AppSettings.DEFAULT_MAX_UPLOAD_FILE_SIZE;
            }

            return fileSize;
        }
    }
    /// <summary>
    /// 单次上传允许的总计文件大小
    /// </summary>
    protected int TotalUploadFileSize
    {
        get
        {
            var fileSize = _appSetting.Attachment?.TotalUploadFileSize ?? AppSettings.DEFAULT_TOTAL_UPLOAD_FILE_SIZE;
            if (fileSize <= 0)
            {
                fileSize = AppSettings.DEFAULT_TOTAL_UPLOAD_FILE_SIZE;
            }

            return fileSize;
        }
    }
    #endregion

    #region 内部成员方法
    /// <summary>
    /// 提交当前操作的内部包装方法, 包含判断是否自动提交
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    protected virtual async Task<int> SaveChangesAsync<TEntitySet>(bool isLogical = false) where TEntitySet : class
    {
        if (AutoSaveChanges)
        {
            var result = await SaveChangesAsync();
            if (result > 0)
            {
                _logger.LogInformation("The user {user} save change {typeName} to database success {isLogical}", UserName, typeof(TEntitySet).GetDisplayName(), GetLogicalString(isLogical));
                await RefreshCacheAsync<TEntitySet>();
            }

            return result;
        }

        return 0;
    }
    /// <summary>
    /// 提交当前操作的内部包装方法, 包含判断是否自动提交, 以及删除附件
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <param name="files">待删除的文件</param>
    /// <returns></returns>
    protected virtual async Task<int> SaveChangesAndDeleteFilesAsync<TEntitySet>(bool isLogical = false, IEnumerable<string>? files = null) where TEntitySet : class
    {
        if (AutoSaveChanges)
        {
            var result = await SaveChangesAsync();
            if (result > 0)
            {
                _logger.LogInformation("The user {user} save change {typeName} to database success {isLogical}", UserName, typeof(TEntitySet).GetDisplayName(), GetLogicalString(isLogical));
                await RefreshCacheAsync<TEntitySet>();
                if (files != null && files.Any())
                {
                    Delete(files);
                }
            }

            return result;
        }

        return 0;
    }
    /// <summary>
    /// 获取是否逻辑操作的字符串
    /// </summary>
    /// <param name="isLogical"></param>
    /// <returns></returns>
    protected virtual string GetLogicalString(bool isLogical = false) => isLogical ? "logical" : "physical";
    /// <summary>
    /// 获取第一个 BusinessType 特性标记的属性的 BusinessType Name
    /// 返回 BusinessType 的值
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <returns></returns>
    protected virtual string GetBusinessType<TEntitySet>()
    {
        var type = typeof(TEntitySet);
        var businessType = type.Name;
        var propertyInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(x => x.HasAttribute<BusinessTypeAttribute>());
        if (propertyInfo != null)
        {
            businessType = propertyInfo.GetAttributeValue<BusinessTypeAttribute, string>(nameof(BusinessTypeAttribute.Name));
            businessType ??= type.Name + AppSettings.CHAR_DOT + propertyInfo.Name;
        }

        return businessType;
    }
    /// <summary>
    /// 获取全部 BusinessType 特性标记的属性的 BusinessType Name 集合
    /// 返回属性 和 BusinessType 的值的集合
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <returns></returns>
    protected virtual IEnumerable<KeyValuePair<string, string>> GetBusinessTypes<TEntitySet>()
    {
        var type = typeof(TEntitySet);
        var dic = new Dictionary<string, string>();
        var propertyInfos = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.HasAttribute<BusinessTypeAttribute>());
        if (propertyInfos != null && propertyInfos.Any())
        {
            foreach (var propertyInfo in propertyInfos)
            {
                var businessType = propertyInfo.GetAttributeValue<BusinessTypeAttribute, string>(nameof(BusinessTypeAttribute.Name));
                businessType ??= type.Name + AppSettings.CHAR_DOT + propertyInfo.Name;
                dic.Add(propertyInfo.Name, businessType);
            }
        }

        return dic;
    }
    /// <summary>
    /// 为当前业务对象设置附件路径
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <param name="attachments">业务数据的附件</param>
    protected virtual void SetBusinessAttachmentPath<TEntitySet, TAttachment>(TEntitySet entitySet, IEnumerable<TAttachment>? attachments) where TAttachment : class, IAttachment<TKey>
    {
        var businessTypes = GetBusinessTypes<TEntitySet>();
        if (businessTypes.Any())
        {
            foreach (var businessType in businessTypes)
            {
                entitySet.SetPropertyValue(businessType.Key, attachments?.FirstOrDefault(x => x.BusinessType == businessType.Value)?.Path);
            }
        }
    }
    /// <summary>
    /// 获取可用外键名或者抛出异常
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TElement">集合元素数据类型</typeparam>
    /// <param name="foreignKey">业务类型对于 TElement 类型的外键</param>
    /// <returns></returns>
    /// <exception cref="ArgumentNullException"></exception>
    protected virtual string GetForeignKey<TEntitySet, TElement>(string? foreignKey = default) where TEntitySet : class, IEntitySet<TKey>
    {
        foreignKey ??= typeof(TElement).GetForeignKey<TEntitySet, TKey>();
        if (string.IsNullOrWhiteSpace(foreignKey))
        {
            throw new ArgumentNullException(nameof(foreignKey), "foreign key can't be null!");
        }

        return foreignKey;
    }
    /// <summary>
    /// 视图模型转换为业务数据对象模型
    /// </summary>
    /// <typeparam name="TEntitySet">业务数据类型</typeparam>
    /// <typeparam name="TModel">业务数据类型或其视图类型</typeparam>
    /// <param name="model">业务数据或其视图</param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    protected virtual TEntitySet ToEntitySet<TEntitySet, TModel>(TModel model) => model switch
    {
        IViewModel<TKey> viewModel => viewModel.Adapt<TEntitySet>(AdapterConfig),
        TEntitySet entitySet => entitySet,
        _ => throw new Exception($"The source type {typeof(TModel).GetDisplayName()} can not be convert to {typeof(TEntitySet).GetDisplayName()}")
    };
    #endregion
}

/// <summary>
/// 公共数据处理服务
/// </summary>
/// <typeparam name="TDbContext">数据库上下文</typeparam>
public class ServiceBase<TDbContext> : ServiceBase<TDbContext, string>, IServiceBase<TDbContext> where TDbContext : DbContext
{
    /// <summary>
    /// 构造方法
    /// </summary>
    /// <param name="logger">日志</param>
    /// <param name="context">数据库上下文</param>
    /// <param name="httpContextAccessor">Http上下文</param>
    /// <param name="cache">缓存</param>
    /// <param name="appSetting">配置</param>
    public ServiceBase(
        TDbContext context,
        ILogger<ServiceBase<TDbContext>> logger,
        IDistributedCache cache,
        IHttpContextAccessor httpContextAccessor,
        AppSetting appSetting
        ) : base(context, logger, cache, httpContextAccessor, appSetting) { }
}